#include "src/base/utils.h"

#include <dirent.h>
#include <linux/limits.h>
#include <sys/stat.h>
#include <unistd.h>

#include <fstream>
#include <string>

namespace fio66::utils {
std::string GetExePath() {
  char dir[PATH_MAX] = {0};
  if (-1 == readlink("/proc/self/exe", dir, PATH_MAX)) {
    return "";
  }
  return std::string(dir);
}

std::string GetExeDir() {
  std::string exe = GetExePath();
  return exe.substr(0, exe.find_last_of("/") + 1);
}

bool PathFileExsist(std::string_view path) {
  // struct stat buffer;
  // return 0 == stat(path.data(), &buffer);
  return std::filesystem::exists(path);
}

bool AppendFile(std::ofstream* des_file, std::string_view src_file_name) {
  std::ifstream src_file(src_file_name.data());
  return AppendFile(des_file, &src_file);
}

bool AppendFile(std::ofstream* des_file, std::ifstream* src_file) {
  if (src_file->is_open()) {
    static char buf[4096] = {0};
    while (src_file->read(buf, _countof(buf))) {
      des_file->write(buf, src_file->gcount());
    }
    return true;
  }
  return false;
}

// std::string GetFormatTime() {
//   time_t t = time(nullptr);
//   tm* now = localtime(&t);
//   char str[50] = {0};
//   strftime(str, sizeof(str), "%Y-%m-%d %H:%M:%S", now);
//   return str;
// }

std::string GetFormatTime() {
  time_t t = time(nullptr);
  tm* now = localtime(&t);
  char str[50] = {0};
  strftime(str, sizeof(str), "%Y-%m-%d-%H-%M-%S", now);
  return str;
}

bool DeleteFile(std::string_view delte_file) { return remove(delte_file.data()) == 0; }

std::optional<std::string> ReadFile(std::string_view file_name) {
  std::ifstream in_file(file_name.data());
  if (!in_file.is_open()) {
    LOGE << "Open file failed:" << file_name;
    return std::nullopt;
  }

  try {
    std::stringstream ss;
    ss << in_file.rdbuf();
    return std::move(ss.str());
  } catch (const std::exception& e) {
    LOGE << "Catch exception:" << e.what();
  }
  return std::nullopt;
}

bool WriteFile(std::string_view file_name, std::string_view data) { return WriteFile(file_name, data.data(), static_cast<uint32_t>(data.size())); }

bool WriteFile(std::string_view file_name, const char* data, const uint32_t size) {
  std::ofstream of(file_name.data());
  if (!of.is_open()) {
    return false;
  }
  return of.write(data, size).good();
}

std::vector<std::filesystem::directory_entry> GetDirectoryFiles(std::string_view dir_name) {
  std::vector<std::filesystem::directory_entry> ret;
  if (!std::filesystem::exists(dir_name) || !std::filesystem::is_directory(dir_name)) {
    LOGE << "Dir empty!";
    return ret;
  }
  
  for(const auto& dir_entry : std::filesystem::recursive_directory_iterator(dir_name)) {
    if(dir_entry.is_regular_file()) {
      ret.push_back(dir_entry);
    }
  }

  return ret;
}

bool CheckPathAccessRight(std::string_view path, const PathAccessRight mode) {
  int32_t access_mode = 0;
  switch (mode) {
    case kAccessRightExist:
      access_mode = F_OK;
      break;
    case kAccessRightRead:
      access_mode = R_OK;
      break;
    case kAccessRightWrite:
      access_mode = W_OK;
      break;
    case kAccessRightExe:
      access_mode = X_OK;
      break;
    default:
      break;
  }
  return 0 == access(path.data(), access_mode);
}
}  // namespace fio66::utils
